package com.chandon.juc.future.completableFuture;

import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * 描述
 *
 * @author Dong
 */

public class TestCompletableFuture01 {

	public static void main(String[] args) throws InterruptedException {

		/* -------------- 基础异步任务编排 SupplyAsync RunAsync -------------- */
		// testSupplyAsync();

		// testRunAsync();

		/* -------------- thenApply thenAccept thenRun -------------- */

		// testThenApply();

		// testThenAccept();

		// testThenRun();

		/* --------------并行任务编排 thenCombine  thenAcceptBoth  runAfterBoth -------------- */
		// 接收结果并且返回结果，可以接收结果没有返回结果，不接收结果也没返回结果

		// testThenCombine();

		// testThenAcceptBoth();

		// testRunAfterBoth();

		// testThenCompose();


		/* -------------- applyToEither，acceptEither，runAfterEither-------------- */
		// 接收结果并且返回结果，可以接收结果没有返回结果，不接收结果也没返回结果

		// testApplyToEither();

		// testAcceptEither();

		// testRunAfterEither();

		/* -------------- 异步任务的异常捕获 exceptionally，thenCompose，handle -------------- */

		// testExceptionally();

		// testWhenComplete();

		// testHandle();


	}

	/**
	 * thenCompose，接收一个参数，返回一个CompletableFuture
	 */
	private static void testThenCompose() {
		CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> {
			return "resultA";

		}).thenCompose(resultA -> {
			System.out.println("taskB");
			CompletableFuture<String> taskC = CompletableFuture.supplyAsync(() -> {
				return resultA + " - resultB";
			});

			return taskC;
		});

		System.out.println(task.join());
	}

	/**
	 * handle可以拿到返回结果的同时，也可以捕获异常，同时也可以返回托底数据。
	 */
	private static void testHandle() {
		CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
			return "taskA";

		}).applyToEither(CompletableFuture.supplyAsync(() -> {
			return "taskB";

		}), resultB -> {
			int i = 1 / 0;
			return resultB.substring(0, 2);

		}).handle((s, throwable) -> {
			System.out.println(throwable.getMessage());
			System.out.println(s);
			return "taskC";

		});

		System.out.println(future.join());
	}

	/**
	 * whenComplete可以拿到返回结果，但因为是Consumer所以本身不返回结果，且不会处理异常而是直接抛出。
	 */
	private static void testWhenComplete() {
		CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
			return "taskA";

		}).thenApply((resultA) -> {
			try {
				TimeUnit.SECONDS.sleep(4);
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
			int n = 1 / 0;
			System.out.println(resultA);
			return resultA;

		}).thenApply(s -> {
			return s.substring(0, 2);

		}).whenComplete((result, ex) -> {
			System.out.println(ex.getMessage());
			System.out.println(result);

		});

		System.out.println(future.join());
	}

	/**
	 * exceptionally
	 * 编排的异步任务中出现异常时才会触发exceptionally执行，可捕获异常，并将异常信息传入，可返回指定结果。
	 * 注意：如果使用Either编排的任务,由于Either的机制，如果正常的任务先于异常的任务执行完成，则不会触发。
	 */
	private static void testExceptionally() {
		String result = CompletableFuture
				.supplyAsync(() -> {
					return "taskA";
				}).thenApply((resultA) -> {

					try {
						TimeUnit.SECONDS.sleep(4);
					} catch (InterruptedException e) {
						throw new RuntimeException(e);
					}
					int n = 1 / 0;
					System.out.println(resultA);
					return resultA;
				}).exceptionally(throwable -> {
					System.out.println(throwable.getMessage());
					return "exceptionally run";
				}).join();

		System.out.println(result);
	}

	private static void testRunAfterEither() {
		CompletableFuture.runAsync(() -> {
			System.out.println("taskA");
		}).runAfterEither(
				CompletableFuture.runAsync(() -> {

					try {
						TimeUnit.SECONDS.sleep(2);
					} catch (InterruptedException e) {
						throw new RuntimeException(e);
					}
					System.out.println("taskB");
				}),
				() -> {
					System.out.println("taskC");
				}
		);

		while (true) {

		}
	}

	private static void testAcceptEither() {
		CompletableFuture.supplyAsync(() -> {
			try {
				TimeUnit.SECONDS.sleep(2);
				System.out.println("taskA");
				return "taskA";
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}).acceptEither(CompletableFuture.supplyAsync(() -> {
					System.out.println("taskB");
					return "taskB";
				}),

				result -> {
					System.out.println("taskC——result:" + result);

				});

		while (true) {

		}
	}

	/**
	 * applyToEither: taskA与taskB并行执行，任何一个task执行完毕就开始执行taskC，并接收一个入参返回一个结果。
	 */
	private static void testApplyToEither() {
		CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> {
			try {
				TimeUnit.SECONDS.sleep(1);
				System.out.println("taskA");
				return "task先执行完成";
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}

		}).applyToEither(CompletableFuture.supplyAsync(() -> {
			System.out.println("taskB");
			return "taskB先执行完成";
		}), result -> {
			System.out.println("taskC");
			return "taskC:" + result;
		});

		String result = task.join();
		System.out.println(result);

		while (true) {

		}
	}

	/**
	 * taskA与taskB并行执行不返回任何结果，执行结束后执行taskC
	 */
	private static void testRunAfterBoth() {
		CompletableFuture.runAsync(() -> {
			System.out.println("taskA");
		}).runAfterBoth(CompletableFuture.runAsync(() -> {
			System.out.println("taskB");
		}), () -> {
			System.out.println("taskC");
		});

		while (true) {

		}
	}

	/**
	 * taskA与taskB并行执行，将2个返回的结果作为参数传给taskC，taskC不返回任何结果
	 */
	private static void testThenAcceptBoth() {
		CompletableFuture.supplyAsync(() -> {
			System.out.println("taskA");
			return "asamiyui ";
		}).thenAcceptBoth(CompletableFuture.supplyAsync(() -> {
			System.out.println("taskB");
			return "daisuki";
		}), (resultA, resultB) -> {
			System.out.println("taskC" + resultA + "-" + resultB);
		});


		while (true) {

		}
	}

	/**
	 * taskA与taskB并行，返回2个结果作为参数传递给taskC，taskC返回一个结果
	 */
	private static void testThenCombine() {
		CompletableFuture<Integer> combine = CompletableFuture.supplyAsync(() -> {
			System.out.println("taskA返回数字78");
			return 78;
		}).thenCombine(CompletableFuture.supplyAsync(() -> {
			System.out.println("taskB返回数字22");
			return 22;
		}), Integer::sum);

		Integer result = combine.join();
		System.out.println(result);
	}

	/**
	 * thenRun(Runable)不接收前任务的结果，也不返回结果
	 */
	private static void testThenRun() {
		CompletableFuture.runAsync(() -> {
			System.out.println("任务A");
		}).thenRun(() -> {
			System.out.println("任务B");
		});

		while (true) {

		}
	}

	/**
	 * taskA生成id并传参给taskB，taskB，打印id不返回结果
	 * thenAccept(Consumer<T>)接收前任务的结果作为入参，不返回结果
	 */
	private static void testThenAccept() {
		CompletableFuture.supplyAsync(() -> {
			return UUID.randomUUID().toString();
		}).thenAccept(System.out::println);

		while (true) {

		}
	}

	/**
	 * 需求：编排一个task，分为2个小task分别为taskA与taskB
	 * taskA生成id，并将id传给taskB，taskB收到id后进行处理后，传给主task并打印。
	 * thenApply(Funciont<T,U>)    T为接收的参数，U为返回的结果。
	 * thenApplyAsync(Funciont<T,U>)
	 * 前者使用同一个线程，后者使用不同的线程。
	 */

	private static void testThenApply() {

		CompletableFuture<String> taskA = CompletableFuture.supplyAsync(() -> {
			String id = UUID.randomUUID().toString();
			System.out.println("taskA生成id:" + id);
			return id;
		});

		CompletableFuture<String> taskB = taskA.thenApply(id -> {
			System.out.println("taskB接收到id:" + id + "  并开始处理");
			return id.replace("-", "");
		});

		/* 简写 */
		CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> {
			String id = UUID.randomUUID().toString();
			System.out.println("taskA生成id:" + id);
			return id;
		}).thenApply(id -> {
			System.out.println("taskB接收到id:" + id + "  并开始处理");
			return id.replace("-", "");
		});


		String result = taskB.join();

		System.out.println("主线程task收到结果:" + result);

		while (true) {

		}
	}

	/**
	 * runAsync()传入Runable与一个线程池，同样的不传入是线程池默认使用ForkJoinPool。
	 */
	private static void testRunAsync() {
		CompletableFuture.runAsync(() -> {
			System.out.println("runable开始");
			System.out.println("runable结束");
		});

		/* 使main线程不结束 */
		while (true) {

		}
	}

	/**
	 * sypplyAsync()传入一个Supplier(U),与一个线程池
	 */
	private static void testSupplyAsync() {
		/*
		如果不提供线程池，默认使用ForkJoinPoll，
		而ForkJoinPoll中默认都是守护线程，main线程结束守护线程也会结束所以直接运行没有任何结果输出
		 */
		CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
			System.out.println("异步任务开始");
			System.out.println("异步任务结束");
			return "异步任务结果";
		});

		/* get()会抛出异常，join()会将异常封装处理掉 */
		String s1 = null;
		try {
			s1 = completableFuture1.get();
		} catch (InterruptedException | ExecutionException e) {
			throw new RuntimeException(e);
		}

		String s2 = completableFuture1.join();

		System.out.println(s1 + "-" + s2);
	}

}
